}
trace!("activating {}", parent.package_id());
- let deps = try!(cx.build_deps(registry, parent, method));
+ let deps = try!(cx.build_deps(registry, &parent, method));
- // Extracting the platform request.
- let platform = match *method {
- Method::Required { target_platform, .. } => target_platform,
- Method::Everything => None,
- };
-
- activate_deps(cx, registry, parent, deps.iter(), 0, &mut |mut cx, registry| {
- cx.visited.remove(id);
- finished(cx, registry)
- })
+ Ok(Some(DepsFrame{
+ parent: parent,
+ remaining_siblings: RcVecIter::new(deps),
- platform: platform.map(|s| s.to_string()),
+ id: id,
+ }))
}
-/// Activates the dependencies for a package, one by one in turn.
-///
-/// This function will attempt to activate all possible candidates for each
-/// dependency of the package specified by `parent`. The `deps` iterator
-/// provided is an iterator over all dependencies where each element yielded
-/// informs us what the candidates are for the dependency in question.
+#[derive(Clone)]
+struct RcVecIter<Elem> {
+ vec: Rc<Vec<Elem>>,
+ next_index: usize,
+}
+
+impl<Elem> RcVecIter<Elem> {
+ fn new(vec: Vec<Elem>) -> RcVecIter<Elem> {
+ RcVecIter {
+ vec: Rc::new(vec),
+ next_index: 0
+ }
+ }
+ fn next(&mut self) -> Option<(usize, &Elem)> {
+ match self.vec.get(self.next_index) {
+ None => None,
+ Some(val) => {
+ let index = self.next_index;
+ self.next_index += 1;
+ Some((index, val))
+ }
+ }
+ }
+}
+
+#[derive(Clone)]
+struct DepsFrame {
+ parent: Rc<Summary>,
+ remaining_siblings: RcVecIter<DepInfo>,
- platform: Option<String>,
+ id: PackageId,
+}
+type RemainingDeps = Vec<DepsFrame>;
+
+struct BacktrackFrame {
+ context_backup: Context,
+ deps_backup: RemainingDeps,
+ remaining_candidates: RcVecIter<Rc<Summary>>,
+ // For building an activation error:
+ parent: Rc<Summary>,
+ cur: usize,
+ dep: Rc<Dependency>,
+ prev_active: Vec<Rc<Summary>>,
+ all_candidates: Vec<Rc<Summary>>,
+}
+
+/// Recursively activates the dependencies for `top`, in depth-first order,
+/// backtracking across possible candidates for each dependency as necessary.
///
/// If all dependencies can be activated and resolved to a version in the
-/// dependency graph the `finished` callback is invoked with the current state
-/// of the world.
-fn activate_deps<'a>(cx: Box<Context>,
- registry: &mut Registry,
- parent: &Summary,
- mut deps: slice::Iter<'a, DepInfo>,
- cur: usize,
- finished: &mut FnMut(Box<Context>, &mut Registry) -> ResolveResult)
- -> ResolveResult {
- let &(dep, ref candidates, ref features) = match deps.next() {
- Some(info) => info,
- None => return finished(cx, registry),
- };
+/// dependency graph, cx.resolve is returned.
+fn activate_deps_loop(mut cx: Context,
+ registry: &mut Registry,
+ top: Rc<Summary>,
+ top_method: &Method) -> CargoResult<Resolve> {
+ let mut backtrack_stack: Vec<BacktrackFrame> = Vec::new();
+ let mut remaining_deps: RemainingDeps = Vec::new();
+ remaining_deps.extend(
+ try!(activate(&mut cx, registry, top, &top_method)));
+ loop {
+ // Retrieves the next dependency to try, from `remaining_deps`.
- let (parent, platform, cur, dep, candidates, features) =
++ let (parent, cur, dep, candidates, features) =
+ match remaining_deps.pop() {
+ None => break,
+ Some(mut deps_frame) => {
+ let info =
+ match deps_frame.remaining_siblings.next() {
+ Some((cur, &(ref dep, ref candidates, ref features))) =>
- (deps_frame.parent.clone(), deps_frame.platform.clone(),
- cur, dep.clone(),
++ (deps_frame.parent.clone(), cur, dep.clone(),
+ candidates.clone(), features.clone()),
+ None => {
+ cx.visited.remove(&deps_frame.id);
+ continue;
+ }
+ };
+ remaining_deps.push(deps_frame);
+ info
+ }
+ };
- let method = Method::Required {
- dev_deps: false,
- features: features,
- uses_default_features: dep.uses_default_features(),
- };
+ let method = Method::Required {
+ dev_deps: false,
+ features: &features,
+ uses_default_features: dep.uses_default_features(),
- target_platform: platform.as_ref().map(|p| p as &str),
+ };
- let prev_active = cx.prev_active(dep);
- trace!("{}[{}]>{} {} candidates", parent.name(), cur, dep.name(),
- candidates.len());
- trace!("{}[{}]>{} {} prev activations", parent.name(), cur,
- dep.name(), prev_active.len());
-
- // Filter the set of candidates based on the previously activated
- // versions for this dependency. We can actually use a version if it
- // precisely matches an activated version or if it is otherwise
- // incompatible with all other activated versions. Note that we define
- // "compatible" here in terms of the semver sense where if the left-most
- // nonzero digit is the same they're considered compatible.
- let my_candidates = candidates.iter().filter(|&b| {
- prev_active.iter().any(|a| a == b) ||
- prev_active.iter().all(|a| {
- !compatible(a.version(), b.version())
- })
- });
+ let prev_active: Vec<Rc<Summary>> = cx.prev_active(&dep).to_vec();
+ trace!("{}[{}]>{} {} candidates", parent.name(), cur, dep.name(),
+ candidates.len());
+ trace!("{}[{}]>{} {} prev activations", parent.name(), cur,
+ dep.name(), prev_active.len());
+
+ // Filter the set of candidates based on the previously activated
+ // versions for this dependency. We can actually use a version if it
+ // precisely matches an activated version or if it is otherwise
+ // incompatible with all other activated versions. Note that we define
+ // "compatible" here in terms of the semver sense where if the left-most
+ // nonzero digit is the same they're considered compatible.
+ let my_candidates: Vec<Rc<Summary>> = candidates.iter().filter(|&b| {
+ prev_active.iter().any(|a| a == b) ||
+ prev_active.iter().all(|a| {
+ !compatible(a.version(), b.version())
+ })
+ }).cloned().collect();
+
+ // Alright, for each candidate that's gotten this far, it meets the
+ // following requirements:
+ //
+ // 1. The version matches the dependency requirement listed for this
+ // package
+ // 2. There are no activated versions for this package which are
+ // semver-compatible, or there's an activated version which is
+ // precisely equal to `candidate`.
+ //
+ // This means that we're going to attempt to activate each candidate in
+ // turn. We could possibly fail to activate each candidate, so we try
+ // each one in turn.
+ let mut remaining_candidates = RcVecIter::new(my_candidates);
+ let maybe_candidate =
+ remaining_candidates.next().map(|(_, candidate)| candidate.clone());
+ let candidate: Rc<Summary> = match maybe_candidate {
+ Some(candidate) => {
+ // We have a candidate. Add an entry to the `backtrack_stack` so
+ // we can try the next one if this one fails.
+ backtrack_stack.push(BacktrackFrame {
+ context_backup: cx.clone(),
+ deps_backup: remaining_deps.clone(),
+ remaining_candidates: remaining_candidates,
+ parent: parent.clone(),
+ cur: cur,
+ dep: dep.clone(),
+ prev_active: prev_active,
+ all_candidates: candidates,
+ });
+ candidate
+ }
+ None => {
+ // This dependency has no valid candidate. Backtrack until we
+ // find a dependency that does have a candidate to try, and try
+ // to activate that one. This resets the `remaining_deps` to
+ // their state at the found level of the `backtrack_stack`.
+ trace!("{}[{}]>{} -- None", parent.name(), cur, dep.name());
+ let last_err = activation_error(&cx, registry, None, &parent, &dep,
+ &prev_active, &candidates);
+ try!(find_candidate(&mut backtrack_stack, &mut cx, &mut remaining_deps,
+ registry, last_err))
+ }
+ };
- // Alright, for each candidate that's gotten this far, it meets the
- // following requirements:
- //
- // 1. The version matches the dependency requirement listed for this
- // package
- // 2. There are no activated versions for this package which are
- // semver-compatible, or there's an activated version which is
- // precisely equal to `candidate`.
- //
- // This means that we're going to attempt to activate each candidate in
- // turn. We could possibly fail to activate each candidate, so we try
- // each one in turn.
- let mut last_err = None;
- for candidate in my_candidates {
trace!("{}[{}]>{} trying {}", parent.name(), cur, dep.name(),
candidate.version());
- let mut my_cx = cx.clone();
- my_cx.resolve.graph.link(parent.package_id().clone(),
- candidate.package_id().clone());
+ cx.resolve.graph.link(parent.package_id().clone(),
+ candidate.package_id().clone());
// If we hit an intransitive dependency then clear out the visitation
// list as we can't induce a cycle through transitive dependencies.